﻿/*
Digraph (library for building, traversal, comparison and saving of directed
graph).

Copyright (C) 2014 Dmitry Merzagitovich Arslanov

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Digraph
{
    /// <summary>
    /// Element of digraph also called node.
    /// </summary>
    /// <typeparam name="TVertex">Type of digraph vertex content.</typeparam>
    /// <typeparam name="TArc">Type of digraph arc content.</typeparam>
    public sealed class Vertex<TVertex, TArc> :
        IEquatable<Vertex<TVertex, TArc>>
    {
        /// <summary>
        /// Data of specified type associated with vertex.
        /// </summary>
        public TVertex Content { get; internal set; }

        internal List<Arc<TVertex, TArc>> IncomingArcList;

        /// <summary>
        /// Arcs incoming to the vertex.
        /// </summary>
        public ReadOnlyCollection<Arc<TVertex, TArc>> IncomingArcs
        {
            get
            {
                return IncomingArcList.AsReadOnly();
            }
        }

        internal List<Arc<TVertex, TArc>> OutcomingArcList;

        /// <summary>
        /// Arcs outcoming from the vertex.
        /// </summary>
        public ReadOnlyCollection<Arc<TVertex, TArc>> OutcomingArcs
        {
            get
            {
                return OutcomingArcList.AsReadOnly();
            }
        }

        /// <summary>
        /// Number of arcs incoming to the vertex.
        /// </summary>
        public int Indegree
        {
            get
            {
                return IncomingArcList.Count;
            }
        }

        /// <summary>
        /// Number of arcs outcoming from the vertex.
        /// </summary>
        public int Outdegree
        {
            get
            {
                return OutcomingArcList.Count;
            }
        }

        /// <summary>
        /// Indicates that vertex with outcoming arcs but without incoming
        /// arcs.
        /// </summary>
        public bool IsSource
        {
            get
            {
                return Indegree == 0 && Outdegree > 0;
            }
        }

        /// <summary>
        /// Indicates that vertex with incoming arcs but without outcoming
        /// arcs.
        /// </summary>
        public bool IsSink
        {
            get
            {
                return Outdegree == 0 && Indegree > 0;
            }
        }

        /// <summary>
        /// Indicates that vertex without incoming and outcoming arcs.
        /// </summary>
        public bool IsIsolated
        {
            get
            {
                return Indegree == 0 && Outdegree == 0;
            }
        }

        internal Vertex(TVertex content)
        {
            Content = content;
            IncomingArcList = new List<Arc<TVertex, TArc>>();
            OutcomingArcList = new List<Arc<TVertex, TArc>>();
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of
        /// the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// True if the current object is equal to the other parameter;
        /// otherwise, false.
        /// </returns>
        public bool Equals(Vertex<TVertex, TArc> other)
        {
            if (other == null) return false;
            return EqualityComparer<TVertex>.Default.Equals(Content,
                                                            other.Content);
        }

        /// <summary>
        /// Determines whether the specified object is equal to the current
        /// object.
        /// </summary>
        /// <param name="obj">
        /// The object to compare with the current object.
        /// </param>
        /// <returns>
        /// True if the specified object is equal to the current object;
        /// otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Vertex<TVertex, TArc>);
        }

        /// <summary>
        /// Serves as the default hash function.
        /// </summary>
        /// <returns>A hash code for the current object.</returns>
        public override int GetHashCode()
        {
            int hashCode = 0;
            if (!EqualityComparer<TVertex>.Default.Equals(Content,
                default(TVertex)))
                hashCode = Content.GetHashCode();
            return hashCode;
        }

        /// <summary>
        /// Determines whether the specified vertex instances are considered
        /// equal.
        /// </summary>
        /// <param name="v1">The first vertex to compare.</param>
        /// <param name="v2">The second vertex to compare.</param>
        /// <returns>
        /// True if the vertices are considered equal; otherwise, false.
        /// If both v1 and v2 are null, the method returns true.
        /// </returns>
        public static bool operator ==(Vertex<TVertex, TArc> v1,
                                       Vertex<TVertex, TArc> v2)
        {
            return Equals(v1, v2);
        }

        /// <summary>
        /// Determines whether the specified vertex instances are considered
        /// not equal.
        /// </summary>
        /// <param name="v1">The first vertex to compare.</param>
        /// <param name="v2">The second vertex to compare.</param>
        /// <returns>
        /// True if the vertices are considered not equal; otherwise, false.
        /// If both v1 and v2 are null, the method returns false.
        /// </returns>
        public static bool operator !=(Vertex<TVertex, TArc> v1,
                                       Vertex<TVertex, TArc> v2)
        {
            return !Equals(v1, v2);
        }

        /// <summary>
        /// Returns a string that represents the current vertex.
        /// </summary>
        /// <returns>A string that represents the current vertex.</returns>
        public override string ToString()
        {
            return string.Format("{0}", Content);
        }
    }
}
